--[[--
    *
    * 文件 : showmsg.lua
    * 功能 : 显示消息
    *
--]]--

local skynet = require "skynet"
print = skynet.error


-- DEBUGLOGPATH
-- timestamp
-- log_timestamp
-- console_msg_log

console_silent = 0

local show  = {}

--------------------------------------------
-- 全局变量 : 消息类型、颜色类型
--------------------------------------------


-- 消息类型
MSG_NONE              = 0     -- 没有的
MSG_STATUS            = 1     -- 状态
MSG_SQL               = 2     -- SQL
MSG_INFORMATION       = 3     -- 一般
MSG_NOTICE            = 4     -- 注意
MSG_WARNING           = 5     -- 警告
MSG_DEBUG             = 6     -- 调试
MSG_ERROR             = 7     -- 错误
MSG_FATALERROR        = 8     -- 严重错误

-- 颜色类型
CL_GREEN              = "\x1b[1;32m"     --  绿色 
CL_PURPLE             = "\x1b[1;35m"     --  紫色 
CL_WHITE              = "\x1b[1;37m"     --  白色 
CL_YELLOW             = "\x1b[1;33m"     --  黄色 
CL_DARKGREEN          = "\x1b[1;36m"     --  深绿 
CL_RED                = "\x1b[1;31m"     --  红色 
CL_RESET              = "\x1b[0m"        --  重置
CL_CLEAR              = "\x1b[2J"        --  清屏


-----------------------------------------------
-- 局部变量 : 日志显示项、控制台隐藏项、消息颜色
-----------------------------------------------


-- 日志显示项 
local LOG = {
    [MSG_WARNING]           = 0X01,  
    [MSG_ERROR]             = 0X02,   
    [MSG_SQL]               = 0x02,
    [MSG_DEBUG]             = 0X04,
    [MSG_FATALERROR]        = 0X08,  
}

-- 控制台隐藏项
local HIDDEN = {
	[MSG_INFORMATION]       = 0X01,
	[MSG_STATUS]            = 0X02,
	[MSG_NOTICE]            = 0X04,
	[MSG_WARNING]           = 0X08,
	[MSG_ERROR]             = 0X10,
	[MSG_DEBUG]             = 0X20,
	[MSG_SQL]               = 0X40,
	[MSG_FATALERROR]        = 0X80,
}

-- 消息颜色
local PREFIX = {
    [MSG_NONE]              = "",                                            -- 没有
    [MSG_STATUS]            = CL_GREEN .. " [Status]: " .. CL_RESET,         -- 状态
    [MSG_SQL]               = CL_PURPLE .. " [SQL]: " .. CL_RESET,           -- SQL
    [MSG_INFORMATION]       = CL_WHITE .. " [Info]: " .. CL_RESET,           -- 一般
    [MSG_NOTICE]            = CL_WHITE .. " [Notice]: " .. CL_RESET,         -- 注意
    [MSG_WARNING]           = CL_YELLOW .. " [Warning]: " .. CL_RESET,       -- 警告
    [MSG_DEBUG]             = CL_DARKGREEN .. " [Debug]: " .. CL_RESET,      -- 调试
    [MSG_ERROR]             = CL_RED .. " [Error]: " .. CL_RESET,            -- 错误
    [MSG_FATALERROR]        = CL_RED .. " [Fatal Error]: " .. CL_RESET,      -- 严重错误
}


----------------------------------------------------------
-- 局部函数
-- 辅助函数(逻辑处理部分)      日期、拼接前缀、显示日志、消息处理
--                          隐藏控制台、显示信息、字符串分割
-----------------------------------------------------------


-- 如果不指定,则默认 %Y/%m/%d %H:%M:%S
local function cur_date(format)

    return os.date(format or "%Y/%m/%d %H:%M:%S")
end 

-- 前缀拼接
local function prefix_cat(flag)

    if PREFIX[flag] then 

        return true, PREFIX[flag]    
    else 

        show:message("In function vshow_message() -> Invalid flag passed.")
        result = false,""  
    end
end 

-- 判断日志文件显示项  由配置文件指定显示项 
local function show_log(flag)

	if LOG[flag] then 
	
	   if (LOG[flag] & console_msg_log) == LOG[flag] then 

		    return true 
	   else 		
		    return false
	   end 
	end 
	
	return false  	
end 

-- 判断控制台隐藏选项，由配置文件制定隐藏项
local function hidden_print(flag)

	if HIDDEN[flag] then 
		
	   if (HIDDEN[flag] & console_silent) == HIDDEN[flag]then 

		   return true 
	   else 		
		   return false
	   end 
	end 
	
	return false	 
end 

-- 截取字符串(写入调试日志先去除颜色编码再写入日志文件)    
-- 例子:    "Hello \x1b[1;31m[错误]: \x1b[0m World"   --> 截取字符串结果:  Hello [错误] World
local function  strsub(str)

    if not str then 
        return 
    end 

    local ret = ""

    local i = string.find(str,"\x1b")
    
    if i then 

        ret = string.sub(str, 1, i-1)                        -- 取前面部分
        local _,k,value = string.find(str, "m(.-)\x1b")      -- 取中间部分
        if value  then 
            ret = ret .. value .. string.sub(str, k+4)       -- 取后面部分
        end 

        return ret                                           -- 拼接返回
    end 
    
    return str -- 匹配不到 则返回源字符串 
end 


-- 显示消息
local function VFPRINTF(output, date, msgtype, fmt, va_list)

    if not fmt then 
        return false 
    end 

    local tempstr = ''

    tempstr = string.format(fmt, table.unpack(va_list)) 

    -- 如果 是文件句柄则写入文件中, 由配置文件指定路径   
    if io.type(output) == "file" then 

        local log_date = ''

         -- 日志配置文件设置log_timestamp
        if log_timestamp then 
            log_date = cur_date(log_timestamp)
        end 
            
         -- 颜色部分不需要写入日志文件     格式如下  年-月-日 时:分:秒  [ 状态 ] :  消息体
        local str = log_date .. strsub(msgtype) .. strsub(tempstr)  .. "\n"      
        output:write(str)    

    else -- 否则控制台显示(俗称黑框) 
        
        local str = date .. msgtype .. tempstr  
        output(str)
    end

    return false      
end 

-- 前缀不添加日期
local function FPRINTF(output,fmg, ...)

    local va_list = {...}
    return VFPRINTF(output, "", "", fmg, va_list)
end 

-- 消息处理
local function vshow_message(flag, str, va_list)

    if not str then 
            show:message("Empty string passed to vshow_message().")
        return true 
    end

    local prefix  = ''
    local date 	  = ''
    local fd 	  = false 
    local result  = false  

    -- 前缀拼接
    result,prefix = prefix_cat(flag)

    if not result then
    	return true 
    end 

    -- 如果定义角色和登录 调试日志文件,由控制台设置   
    if DEBUGLOGCHAR or DEBUGLOGLOGIN then 
        fd = true 
    end 

    -- MSG_NONE 消息无需日期做前缀 
    if flag ~= MSG_NONE then 
  
        if timestamp then 
	        date = cur_date(timestamp)   -- timestamp 由配置文件指定		 
        end 
    end 
 
    -- 判断控制台隐藏选项
    if not hidden_print(flag) then 

     	VFPRINTF(print, date, prefix, str, va_list) -- 打印消息
    end 

    -- 如果设置调试日志
    if fd then  
     
        if DEBUGLOGPATH and string.len(DEBUGLOGPATH) > 0 then   
          
      	        fd  = io.open(DEBUGLOGPATH,"a")  --以追加的方式打开文件

                if not fd then 
             
                    FPRINTF(print, CL_RED .. " Could not open %s , access denied" .. CL_RESET, DEBUGLOGPATH) -- 错误或无效的路径
                else 	

		                if show_log(flag) then  -- 判断日志显示选项	
				
			                VFPRINTF(fd, date, prefix, str, va_list)				  	 		
		                end 
                    
                        fd:close()
                end 
        else 
                FPRINTF(print, CL_RED .. " DEBUGLOGPATH not defined!" .. CL_RESET)
        end 
    end 

    return false 
end 


--------------------------------------------------------------------
-- 全局函数
-- 函数调用部分:   清屏、提示信息、状态信息、SQL错误信息、错误信息
--               注意信息、调试信息、严重错误信息、警告信息
--------------------------------------------------------------------

-- 调用者指定 flag 标志 
function show:_message(flag, str,...)

    local va_list = {...}
    local ret     = vshow_message(flag, str, va_list)
    return ret
end 

-- 清屏(刷新黑框)
function show:clear_screen()

   self.message(CL_CLEAR)
end 

-- 不显示前缀  打印信息
function show:message(str, ...)

    local va_list = {...}
    local ret     = vshow_message(MSG_NONE, str, va_list)
    return ret
end

--状态信息
function show:status(str, ...)

    local va_list = {...}
    local ret     = vshow_message(MSG_STATUS, str, va_list)
    return ret
end 

--sql信息
function show:SQL(str, ...)

    local va_list = {...}
    local ret     = vshow_message(MSG_SQL, str, va_list)
    return ret
end 


-- 一般显示
function show:info(str, ...)
    local va_list = {...}
    local ret     = vshow_message(MSG_INFORMATION, str, va_list)
    return ret
end 

-- 提示信息
function show:notice(str, ...)
    local va_list = {...}
    local ret     = vshow_message(MSG_NOTICE, str, va_list)
    return ret
end

--调试信息
function show:debug(str, ...)

    local va_list = {...}
    local ret     = vshow_message(MSG_DEBUG, str, va_list)
    return ret
end 

--错误信息
function show:error(str, ...)

    local va_list = {...}
    local ret     = vshow_message(MSG_ERROR, str, va_list)
    return ret
end 

--致命错误
function show:falat_error(str, ...)

    local va_list = {...}
    local ret     = vshow_message(MSG_FATALERROR, str, va_list)
    return ret
end  

-- 警告信息
function show:warning(str, ...)

    local va_list = {...}
    local ret     = vshow_message(MSG_WARNING, str, va_list)
    return ret
end 

function show:init()   
end 


return show

